package com.snail.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.snail.common.core.constant.CommonConstants;
import com.snail.common.core.exception.ServiceException;
import com.snail.common.core.utils.DateUtils;
import com.snail.common.core.utils.NumberUtil;
import com.snail.common.core.utils.PageUtils;
import com.snail.common.core.utils.StringUtils;
import com.snail.common.core.web.page.PageResult;
import com.snail.common.redis.service.RedisService;
import com.snail.common.sequence.config.SequenceProperties;
import com.snail.common.sequence.constants.SequenceConstants;
import com.snail.system.api.domain.SysSequenceRule;
import com.snail.system.api.dto.SequenceRuleDto;
import com.snail.system.api.query.SysSequenceRuleQuery;
import com.snail.system.mapper.SysSequenceRuleMapper;
import com.snail.system.service.ISysSequenceRuleService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @Description: 序列号规则业务实现
 * @Author: Snail
 * @CreateDate: 2023/8/21 10:53
 * @Version: V1.0
 */
@Service
public class SysSequenceRuleServiceImpl extends ServiceImpl<SysSequenceRuleMapper, SysSequenceRule> implements ISysSequenceRuleService {

    @Autowired
    private SequenceProperties sequenceProperties;

    @Autowired
    private RedisService redisService;

    @Override
    public PageResult<SysSequenceRule> selectSysSequenceRulePage(SysSequenceRuleQuery query) {
        Page<SysSequenceRule> page = PageUtils.buildPage(query);
        LambdaQueryWrapper<SysSequenceRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotEmpty(query.getSequenceName()), SysSequenceRule::getSequenceName, query.getSequenceName());
        queryWrapper.like(StringUtils.isNotEmpty(query.getSequenceCode()), SysSequenceRule::getSequenceCode, query.getSequenceCode());
        queryWrapper.ge(StringUtils.isNotEmpty(query.getBeginTime()), SysSequenceRule::getCreateTime, query.getBeginTime());
        queryWrapper.le(StringUtils.isNotEmpty(query.getEndTime()), SysSequenceRule::getCreateTime, query.getEndTime());
        queryWrapper.orderByDesc(SysSequenceRule::getCreateTime);
        page = baseMapper.selectPage(page, queryWrapper);
        return PageUtils.pageResult(page);
    }

    @Override
    public SysSequenceRule getSysSequenceRuleById(String ruleId) {
        return getById(ruleId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteSysSequenceRuleByIds(String[] ruleIds) {
        int batch = baseMapper.deleteBatchIds(Arrays.asList(ruleIds));
        return batch == ruleIds.length;
    }

    @Override
    public String createChildrenSequenceRule(SequenceRuleDto sequenceRuleDto) {
        String sequenceCode = sequenceRuleDto.getSequenceCode();
        String childSequenceCode = sequenceRuleDto.getChildSequenceCode();
        if (StringUtils.isEmpty(sequenceCode) || StringUtils.isEmpty(childSequenceCode)) {
            throw new ServiceException("序列号和子序列号不能为空!");
        }
        //redis中获取序列号规则
        SysSequenceRule rule = getSysSequenceRule(sequenceCode);
        if (rule == null) {
            throw new ServiceException(StringUtils.format("序列号{}规则不存在！", sequenceCode));
        }
        //复制复制父序列号规则
        SysSequenceRule child = new SysSequenceRule();
        BeanUtils.copyProperties(rule,child,"ruleId");
        child.setSequenceCode(childSequenceCode);
        child.setSequenceName(rule.getSequenceName() + "的子序列号" + childSequenceCode);
        child.setCurrentValue(0L);
        child.setMaxSequence(null);
        child.setParentSequenceCode(sequenceCode);

        //保存新的序列号规则
        insertSequenceRule(child);

        return generate(childSequenceCode);
    }

    /**
     * 生产序列号
     *
     * @param sequenceCode 序列号编码
     * @return 结果
     */
    @Override
    public String generate(String sequenceCode) {
        //redis中获取序列号规则
        SysSequenceRule rule = getSysSequenceRule(sequenceCode);
        //根据规则生成序列号
        List<String> sequenceList = generateSequenceList(rule);
        //获取第一个序列号返回
        String sequence = sequenceList.remove(0);
        //3.保存到redis
        saveSequenceConfig(rule, sequenceList);
        return sequence;
    }

    @Override
    public boolean generateBatch(String sequenceCode, Long generateNum) {
        //获取序列号配置
        SysSequenceRule rule = getSysSequenceRule(sequenceCode);
        Long ruleGenerateNum = rule.getGenerateNum();
        //批量生成数量
        rule.setGenerateNum(generateNum);
        //根据规则生成序列号
        List<String> sequenceList = generateSequenceList(rule);
        //保存序列号配置
        rule.setGenerateNum(ruleGenerateNum);
        saveSequenceConfig(rule, sequenceList);
        return true;
    }

    /**
     * 获取序列号配置规则
     * 首先从redis获取，然后从数据库获取
     *
     * @param sequenceCode 序列号编码
     * @return 结果
     */
    private SysSequenceRule getSysSequenceRule(String sequenceCode) {
        //redis中获取序列号规则
        String ruleJson = redisService.getCacheObject(StringUtils.format(SequenceConstants.SEQUENCE_CONFIG_KEY, sequenceCode));
        SysSequenceRule rule;
        //redis没有从数据库获取
        if (StringUtils.isEmpty(ruleJson)) {
            LambdaQueryWrapper<SysSequenceRule> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysSequenceRule::getSequenceCode, sequenceCode);
            rule = baseMapper.selectOne(queryWrapper);
        } else {
            rule = JSON.parseObject(ruleJson, SysSequenceRule.class);
        }
        if (rule == null) {
            throw new ServiceException("没有该序列号生成的规则!");
        }
        return rule;
    }

    /**
     * 保存序列号配置
     *
     * @param rule         序列号规则
     * @param sequenceList 序列号集合
     */
    private void saveSequenceConfig(SysSequenceRule rule, List<String> sequenceList) {
        //3.保存到redis
        String sequenceCode = StringUtils.format(SequenceConstants.SEQUENCE_CODE_KEY, rule.getSequenceCode());
        redisService.setCacheList(sequenceCode, sequenceList);
        //更新数据库
        LambdaUpdateWrapper<SysSequenceRule> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(SysSequenceRule::getCurrentValue, rule.getCurrentValue());
        updateWrapper.set(SysSequenceRule::getMaxSequence, sequenceList.get(sequenceList.size() - 1));
        updateWrapper.eq(SysSequenceRule::getRuleId, rule.getRuleId());
        this.update(updateWrapper);
        //将更新的规则信息保存到redis
        redisService.setCacheObject(StringUtils.format(SequenceConstants.SEQUENCE_CONFIG_KEY, rule.getSequenceCode()), JSON.toJSONString(rule));
    }

    /**
     * 生成序列号列表
     *
     * @param rule 序列号规则
     * @return 结果
     */
    private List<String> generateSequenceList(SysSequenceRule rule) {
        //最小值
        Long minValue = rule.getMinValue();
        //最大值
        Long maxValue = rule.getMaxValue();
        //当前值
        Long currentValue = rule.getCurrentValue() < minValue ? minValue - 1 : rule.getCurrentValue();
        //每次生成多少个(配置的预生产 - 已经生成的)
        Long generateNum = rule.getGenerateNum();
        //日期格式
        String dateFormat = rule.getDateFormat();
        //生成的序列号
        List<String> numbers = new ArrayList<>();
        for (int i = 0; i < generateNum; i++) {
            currentValue++;
            //当前值已经大于了最大值，不进行重置
            if (currentValue > maxValue && CommonConstants.NO.equals(rule.getReset())) {
                break;
            }
            //当前值已经大于了最大值，不进行重置
            if (currentValue > maxValue && CommonConstants.YES.equals(rule.getReset())) {
                currentValue = minValue + 1;
            }
            StringBuilder sequence = new StringBuilder(String.valueOf(currentValue));
            if (StringUtils.isNotEmpty(rule.getRepairValue())) {
                //需要补值多少个
                int bit = NumberUtil.bit(maxValue) - NumberUtil.bit(currentValue);
                //进行补值
                while (bit > 0) {
                    sequence.insert(0, rule.getRepairValue());
                    bit--;
                }
            }
            //2.日期+数字
            if (SequenceConstants.STRATEGY_2.equals(rule.getStrategy())) {
                sequence.insert(0, DateUtils.parseDateToStr(dateFormat, new Date()));
            }
            //添加前缀
            if (StringUtils.isNotEmpty(rule.getPrefix())) {
                sequence.insert(0, rule.getPrefix());
            }
            //添加后缀
            if (StringUtils.isNotEmpty(rule.getSuffix())) {
                sequence.insert(sequence.length(), rule.getSuffix());
            }
            numbers.add(sequence.toString());
        }
        if (CollectionUtil.isEmpty(numbers)) {
            throw new ServiceException("序列号数字已达到最大值!");
        }
        rule.setCurrentValue(currentValue);
        rule.setMaxSequence(numbers.get(numbers.size() - 1));
        return numbers;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String insertSequenceRule(SysSequenceRule rule) {
        StringBuilder error = new StringBuilder();
        if (SequenceConstants.STRATEGY_2.equals(rule.getStrategy())) {
            if (StringUtils.isEmpty(rule.getDateFormat())) {
                error.append("日期格式不能为空!");
            }
        }
        if (StringUtils.isNotEmpty(error)) {
            throw new ServiceException(error.toString());
        }
        save(rule);
        //根据规则生成序列号
        List<String> sequenceList = generateSequenceList(rule);
        //保存序列号配置
        redisService.deleteObject(StringUtils.format(SequenceConstants.SEQUENCE_CODE_KEY, rule.getSequenceCode()));
        saveSequenceConfig(rule, sequenceList);
        return rule.getRuleId();
    }

    @Override
    public boolean updateSequenceRule(SysSequenceRule rule) {
        this.updateById(rule);
        //根据规则生成序列号
        List<String> sequenceList = generateSequenceList(rule);
        //保存序列号配置
        redisService.deleteObject(StringUtils.format(SequenceConstants.SEQUENCE_CODE_KEY, rule.getSequenceCode()));
        saveSequenceConfig(rule, sequenceList);
        return true;
    }


}
